package com.cenxi.common.aspect;

import com.cenxi.common.aspect.annotation.OpaLog;
import com.cenxi.dao.entity.OperateLog;
import com.cenxi.dao.mapper.OperateLogMapper;
import com.cenxi.manage.ManageOperateservice;
import com.cenxi.utils.HttpUtil;
import com.cenxi.utils.subject.LoginSubject;
import com.cenxi.utils.subject.SecurityContextUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;

/**
 * @Author cenxi
 * @Date 2023/11/13 16:14
 * @Version 1.0
 */

@Aspect
@Component
@Slf4j
public class PreOpaLogAspect {
    @Autowired
    private HttpServletRequest request;
    private final ObjectMapper objectMapper;

    @Autowired
    private ManageOperateservice manageOperateservice;
    /**
     * 构建
     * @param objectMapper
     */
    public PreOpaLogAspect(ObjectMapper objectMapper) {
        SimpleModule simpleModule = new SimpleModule();
        this.objectMapper = objectMapper.registerModule(simpleModule);
    }

    /**
     * 定义AOP签名 (切入所有使用鉴权注解的方法)
     */
    public static final String POINTCUT_SIGN = "@annotation(com.cenxi.common.aspect.annotation.OpaLog)";

    /**
     * 声明AOP签名
     */
    @Pointcut(POINTCUT_SIGN)
    public void pointcut() {
    }

    /**
     * 方法调用之前调用，接口权限校验
     * @param joinPoint 切入点
     */
    @Before("pointcut()")
    public void doBeforeMethod(JoinPoint joinPoint) {
        String requestUri = HttpUtil.getFullRequestURI(request);
        log.info("===========@Before：：请求URL：{}，请求方法：{}，请求参数：{}", requestUri, joinPoint.getSignature(), getRequestParam(joinPoint));
    }

    /**
     * 环绕切入
     *
     * @param joinPoint 切面对象
     * @return 底层方法执行后的返回值
     * @throws Throwable 底层方法抛出的异常
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //获取开始执行的时间
        long startTime = System.currentTimeMillis();
        String requestUri = HttpUtil.getFullRequestURI(request);
        Object proceed = joinPoint.proceed();
        // 获取执行结束的时间
        long endTime = System.currentTimeMillis();
        // 打印耗时的信息
        String ip = HttpUtil.getRemoteAddress(request);
        log.info("===========@Around：：请求地址：{},请求URL：{}，请求方法：{}，本次会话共耗时：{}毫秒",ip, requestUri, joinPoint.getSignature(), endTime - startTime);
        recordOperationLog(joinPoint,endTime-startTime);
        return proceed;
    }

    /**
     * 方法之后调用
     * @param joinPoint   切入点
     * @param returnValue 方法返回值
     */
    @AfterReturning(pointcut = "pointcut()", returning = "returnValue")
    public void doAfterControllerMethodReturning(JoinPoint joinPoint, Object returnValue) {
        String requestUri = HttpUtil.getFullRequestURI(request);
        if(returnValue==null){
            return;
        }
        if (!Objects.equals(returnValue.getClass(), Void.class)) {
            String response;
            try {
                response = objectMapper.writeValueAsString(returnValue);
            } catch (JsonProcessingException e) {
                response = returnValue.toString();
            }
            log.info("===========@After：：请求URL：{}，请求方法：{}，响应内容：{}", requestUri, joinPoint.getSignature(), response);
        }
    }

    /**
     * 操作日志记录
     * @param joinPoint
     */
    protected void recordOperationLog(JoinPoint joinPoint,long time){
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        OpaLog  logger = method.getAnnotation(OpaLog.class);
        if(StringUtils.isNotBlank(logger.value())){
            LoginSubject loginUser = SecurityContextUtil.getLoginUser();
            OperateLog operateLog = new OperateLog();
            operateLog.setOperateName(logger.value());
            operateLog.setIp(HttpUtil.getRemoteAddress(request));
            operateLog.setAccount(loginUser.getAccount());
            operateLog.setAccountId(loginUser.getAccountId());
            operateLog.setOperateUrl(request.getRequestURI());
            manageOperateservice.save(operateLog);
            log.info("操作记录--> {}", logger.value());
        }

    }


    private String getRequestParam(JoinPoint joinPoint){
        Object[] argsBefore = joinPoint.getArgs();
        Object[] args = new Object[argsBefore.length];
        // 移除无法被序列化的参数
        for (int i = 0; i < argsBefore.length; i++) {
            Object argBefore = argsBefore[i];
            if (argBefore instanceof ServletRequest || argBefore instanceof ServletResponse
                    || argBefore instanceof MultipartFile) {
                continue;
            }
            args[i] = argBefore;
        }
        String parameter;
        try {
            parameter = objectMapper.writeValueAsString(args);
        } catch (Exception e) {
            parameter = Arrays.toString(args);
        }
        return parameter;
    }
}
